namespace Bowlus.FrontEnd
{
    using System;
    using System.Diagnostics;
    using System.Linq;
    using System.Reactive.Linq;

    using Bowlus.Common;
    using Bowlus.FrontEnd.RequestRouting;

    using Microsoft.Practices.Unity;
    using Microsoft.WindowsAzure;
    using Microsoft.WindowsAzure.Diagnostics;
    using Microsoft.WindowsAzure.ServiceRuntime;

    public class WebRole : RoleEntryPoint
    {
        private readonly IObservable<long> checkStateTimer;

        private RoutingManager requestRoutingManager;

        public WebRole()
        {
            this.checkStateTimer = Observable.Interval(Settings.TenantTablePollingInterval);
        }

        public override bool OnStart()
        {
            ConfigureDiagnostics();

            RoleEnvironment.Changing += RoleEnvironmentChanging;
            RoleEnvironment.Changed += this.RoleEnvironmentChanged;

            try
            {
                BowlusLogger.LogInfo("Starting front end web role.");
                var unityContainer = BowlusContainer.Current.GetInstance<IUnityContainer>();
                ContainerBootstrapper.RegisterTypes(unityContainer);
                this.requestRoutingManager = unityContainer.Resolve(typeof(RoutingManager)) as RoutingManager;

                if (this.requestRoutingManager != null)
                {
                    this.requestRoutingManager.StatusChange += this.RequestRoutingManager_StatusChange;

                    this.requestRoutingManager.Initialize();
                    BowlusLogger.LogInfo("Started front end web role.");
                }
                else
                {
                    BowlusLogger.LogError("Cannot get an instance of RoutingManager");
                }
            }
            catch (Exception ex)
            {
                BowlusLogger.LogError(ex.ToString());
            }

            return base.OnStart();
        }

        public override void OnStop()
        {
            this.requestRoutingManager.ClearConfiguration();
            base.OnStop();
        }

        public override void Run()
        {
            this.checkStateTimer.Subscribe(s => this.requestRoutingManager.Run());
            base.Run();
        }

        private static void ConfigureDiagnostics()
        {
            var config = DiagnosticMonitor.GetDefaultInitialConfiguration();

            CrashDumps.EnableCollection(true);

            config.DiagnosticInfrastructureLogs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);

            // For windows azure logs
            config.Logs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);
            config.Logs.ScheduledTransferLogLevelFilter = LogLevel.Verbose;

            var cloudStorageAccount = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString"));

            DiagnosticMonitor.Start(cloudStorageAccount, config);
            Trace.Listeners.Add(new DiagnosticMonitorTraceListener { Filter = new EventTypeFilter(SourceLevels.All) });

            BowlusLogger.LogInfo("Configured Windows Azure Diagnostics");
        }

        private static void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e)
        {
            if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange))
            {
                e.Cancel = e.Changes.OfType<RoleEnvironmentConfigurationSettingChange>().Any(Settings.ShouldRecycleInstance);
            }
            else if (e.Changes.Any(change => change is RoleEnvironmentTopologyChange))
            {
                e.Cancel = false;
            }

            // Log the Changing Event
            foreach (var change in e.Changes)
            {
                BowlusLogger.LogInfo(string.Format("RoleEnvironment Instant {0} - Changing Event: {1}", RoleEnvironment.CurrentRoleInstance.Id, change));
            }
        }

        private void RoleEnvironmentChanged(object sender, RoleEnvironmentChangedEventArgs e)
        {
            if (!e.Changes.Any(change => change is RoleEnvironmentTopologyChange))
            {
                return;
            }

            BowlusLogger.LogInfo("TOPOLOGY CHANGE DETECTED.");

            foreach (var topologyChange in e.Changes.OfType<RoleEnvironmentTopologyChange>().Where(tc => tc.RoleName.Equals(Settings.AppHostRoleName)))
            {
                BowlusLogger.LogInfo(string.Format("Topology change {0}", topologyChange.RoleName));
                this.requestRoutingManager.OnAppHostsChange();
            }
        }

        private void RequestRoutingManager_StatusChange(object sender, RequestRoutingManagerEventArgs e)
        {
            BowlusLogger.LogInfo(e.StatusMessage);
        }
    }
}
